import java.io.*;
import java.net.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Scanner;

public class Server {
        List<Socket> connectionList;
        int port;
        ServerSocket outSocket;
        DataOutputStream out;
        DataInputStream in;
        boolean blockIO;

Server(int chosenPort){
        try{
                outSocket = new ServerSocket(chosenPort);
                outSocket.setSoTimeout(10000);
                port = outSocket.getLocalPort();
                connectionList = Collections.synchronizedList(new ArrayList<Socket>());
                
                new Thread(this.new AcceptThread()).start();
                new Thread(this.new ListenThread()).start();
                System.out.println("Server deployed on port " + port + ".  Listening for new connections...");
                
        }catch(SocketException e){
                System.out.println("Failed to bind port");
                System.exit(1);
        }catch(IOException io){
                System.out.println("IO error in constructor");
                System.exit(2);
        }
}

/*
 * This thread continuously listens for connection requests and adds them to the connections list.
 */
private class AcceptThread implements Runnable{
        public void run(){
                while(true){
                        try{
                                Socket temp = outSocket.accept();
                                
                                connectionList.add(temp);
                                /*DataInputStream nameStream = new DataInputStream(connectionList.get(connectionList.size()-1).getInputStream());
                                System.out.println(nameStream.readUTF() + " connected");
                                nameStream.close();*/
                        }catch(SocketException se){
                        	System.out.println("Socket error in accept thread");
                        }catch(IOException io){
                                System.out.println("IO Error in accept thread");
                        }
                }
        }
}
        
private void cleanup(int clientID){
                
        while(blockIO);
        
        blockIO = true;
        connectionList.remove(clientID);
        blockIO = false;

}

private class ListenThread implements Runnable{  
        public void run(){
                int clientID = 0;
                try{
                        
                        String data;
                        while(true){
                                
                                for(clientID = 0; clientID < connectionList.size(); clientID++){
                                        in = new DataInputStream(connectionList.get(clientID).getInputStream());
                                        if(in.available() == 0)
                                                continue;
                                        data = in.readUTF();
                                        System.out.println("Sending: " + data);
                                        broadCastToClients(data, clientID);
                                        
                                }
                        }
                }catch(SocketException se){
                        System.out.println("Unhandled socket exception in listen/broadcast");
                }catch(IOException io){
                        System.out.println("IO Error in listen/broadcast");
                }
         }
 }

public void broadCastToClients(String message, int sender) throws IOException{
		
	int i;
        for(i = 0; i < connectionList.size(); i++){
        	try{
                if(i != sender){
	                out = new DataOutputStream(connectionList.get(i).getOutputStream());
	                out.writeUTF(message);
                }
        	}catch(SocketException se){
        		
        		System.out.println("Client " + ++i + " presumably left.  Removing from send list");
        		connectionList.remove(i);
        		i-=2;		//subtract 1 from i to account for the removed client.  Else the next entry will be skipped.
        	}
        }
        //out.flush();
}

public void broadCastToClients(String message) throws IOException{
        for(int i = 0; i < connectionList.size(); i++){
                out = new DataOutputStream(connectionList.get(i).getOutputStream());
                out.writeUTF(message);
        }
        //out.flush();
}

public void sendToOneClient(String message, int recipient) throws IOException{
        out = new DataOutputStream(connectionList.get(recipient).getOutputStream());
        out.writeUTF(message);
        //out.flush();
}

public static void main(String [] args){
        
        //Server stuff = new Server();
        Server stuff = new Server(7777);        //easier for testing to assign the port
        
        try{
                
                Scanner scanner = new Scanner(System.in);
                String message;
                do{
                        message = scanner.nextLine();
                        stuff.broadCastToClients(message);
                }while(!message.contains("shutdown"));
                scanner.close();
                System.exit(0);
                
        }catch(Exception e){
                System.out.println("Something broke");
                e.printStackTrace();
        }
}
}